#include "stdafx.h"
#include "net/ICMPSocketImpl.hpp"
#include "stdlib/timestamp.hpp"
#include "stdlib/timespan.hpp"
using JHCPP::stdlib::CTimeSpan;
using JHCPP::stdlib::CTimeStamp;

NET_NAMESPACE_BEGIN

CICMPSocketImpl::CICMPSocketImpl(CIPAddress::Family family, int dataSize, int ttl, int timeout)
	: CRawSocketImpl(family, IPPROTO_ICMP),
	m_icmpPacket(family, dataSize),
	m_timeout(timeout)
{
	setOption(IPPROTO_IP, IP_TTL, ttl);
	setReceiveTimeout(CTimeSpan(timeout));
}

CICMPSocketImpl::~CICMPSocketImpl()
{
}

int CICMPSocketImpl::sendTo(const void*, int, const CSocketAddress& address, int flags)
{
	int n = CSocketImpl::sendTo(m_icmpPacket.packet(), m_icmpPacket.packetSize(), address, flags);
	return n;
}

int CICMPSocketImpl::receiveFrom(void*, int, CSocketAddress& address, int flags)
{
	int maxPacketSize = m_icmpPacket.maxPacketSize();
	unsigned char* buffer = new unsigned char[maxPacketSize];

	try
	{
		CTimeStamp ts;
		do
		{
			if (ts.isElapsed(m_timeout)) 
			{
				// This guards against a possible DoS attack, where sending
				// fake ping responses will cause an endless loop.
				throw TimeoutException();
			}
			CSocketImpl::receiveFrom(buffer, maxPacketSize, address, flags);
		} while (!m_icmpPacket.validReplyID(buffer, maxPacketSize));
	}
	catch (TimeoutException&)
	{
		delete [] buffer;			
		throw;
	}
	catch (CException&)
	{
		std::string err = m_icmpPacket.errorDescription(buffer, maxPacketSize);
		delete [] buffer;
		if (!err.empty()) 
			throw ICMPException(err);
		else 
			throw;
	}

	struct timeval then = m_icmpPacket.time(buffer, maxPacketSize);
	struct timeval now  = m_icmpPacket.time();

	int elapsed	= (((now.tv_sec * 1000000) + now.tv_usec) - ((then.tv_sec * 1000000) + then.tv_usec))/1000;

	delete[] buffer;
	return elapsed;
}


NET_NAMESPACE_END
